//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include <stdexcept>
#include <omnetpp.h>
#include "BitTorrentFile.h"
#include "FilePiece.h"


namespace peermaliziosi {

long BitTorrentFile::countConstructors=0;
long BitTorrentFile::countDestructors=0;

BitTorrentFile::BitTorrentFile(int size) {

    numPieces= size;
    cModule *sysM= simulation.getSystemModule();
    numBlocks= sysM->par("numBlocks").longValue();

    have=false;
    pieces=new FilePiece[numPieces];

    countBlocks= 0;

    countConstructors++;
}

BitTorrentFile::~BitTorrentFile() {
    delete []pieces;

    countDestructors++;
}

bool BitTorrentFile::setPiece(int pieceIndex, bool val) {
    if (checkIndex(pieceIndex)) {
        if (val) {
            // imposto tutto a true
            bool ret= true;
            for (int b=0; b<numBlocks; b++) {
                if (!pieces[pieceIndex].setBlock(b)) {
                    // se non avevo questo blocco, incremento il contatore
                    countBlocks++;
                    ret= false;
                }
            }
            return ret;
        } else {
            // imposto tutto a false (implementazione diversa rispetto al caso val==true, ma va be')§
            bool ret= true;
            for (int b=0; b<numBlocks; b++) {
                if (pieces[pieceIndex].haveBlock(b)) {
                    // se avevo questo blocco, decremento il contatore
                    countBlocks--;
                } else {
                    ret= false;
                }
            }
            pieces[pieceIndex].setFalseAllPieceBlocks();
            return ret;
        }
    } else {
        throw std::range_error("Indice di pezzo errato (setBlock)");
    }
}

bool BitTorrentFile::setBlock(int pieceIndex, int blockIndex) {
    if (checkIndex(pieceIndex)) {
        bool ret= pieces[pieceIndex].setBlock(blockIndex);
        if (!ret) {
            // se non avevo questo blocco, incremento il contatore
            countBlocks++;
        }
        return ret;
    } else {
        throw std::range_error("Indice di pezzo errato (setBlock)");
    }
}

bool BitTorrentFile::haveBlock(int pieceIndex, int blockIndex) {
    if (checkIndex(pieceIndex)) {
        return pieces[pieceIndex].haveBlock(blockIndex);
    } else {
        throw std::range_error("Indice di pezzo errato (haveBlock)");
    }
}

bool BitTorrentFile::havePiece(int pieceIndex) {
    if (checkIndex(pieceIndex)) {
        return pieces[pieceIndex].havePiece();
    } else {
        throw std::range_error("Indice di pezzo errato (havePiece)");
    }
}

bool BitTorrentFile::haveTorrent() {
    // restituisco true se possiedo tutti i pezzi
    return (countBlocks == numPieces*numBlocks);
}

int BitTorrentFile::getNumBlocksPerPiece() {
    return numBlocks;
}

int BitTorrentFile::getNumPieces() {
    return numPieces;
}

int BitTorrentFile::getTotBlocks() {
    return numPieces*numBlocks;
}

bool BitTorrentFile::checkIndex(int pieceIndex, int blockIndex) {
    if (checkIndex(pieceIndex)) {
        return pieces[pieceIndex].checkIndex(blockIndex);
    } else {
        return false;
    }
}

bool BitTorrentFile::checkIndex(int pieceIndex) {
    if (0<=pieceIndex && pieceIndex<numPieces) {
        return true;
    } else {
        return false;
    }
}

void BitTorrentFile::initTorrentFile(bool bitfield[], double d) {
    // TODO pericolo segmentation fault se usato in modo non corretto
    for (int p=0; p<numPieces; p++) {

        bool val= bernoulli(d)==1;
        setPiece(p, val);
        bitfield[p]= val;
    }
}

const char* BitTorrentFile::toString() {
    strBitfield= "";

    int nb= pieces[0].getNumBlocks();

    for (int p=0; p<numPieces; p++) {
        strBitfield+= "|";
        for (int b=0; b<nb; b++) {
            if (pieces[p].haveBlock(b)) {
                strBitfield+= "+";
            } else {
                strBitfield+= ".";
            }
        }
    }
    strBitfield+= "|";

    return strBitfield.c_str();
}

float BitTorrentFile::getCompletation() {
    if (pieces==NULL) return 0.0;
    else return (float) countBlocks / (numPieces*numBlocks);
}

}
